package com.zcp;


import com.zcp.pcsuitall.MyDes;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import java.util.concurrent.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 主要用来测试从手机sd卡中批量下载文件。
 * 
 * @author Administrator
 */
public class DownFile {

    private static String path ="/sdcard/scj/kk/";
        //"/mnt/sdcard/dcim/.thumbnails";
        //"/mnt/sdcard/dcim/Camera"; 
    private static String cipherKey="asdfghjk";
    public static void main(String[] args) {
        // 重定向端口
        try {
            Runtime.getRuntime()
                    .exec("E:\\work\\soft\\android-sdk-windows\\android-sdk-windows\\tools\\adb.exe forward tcp:8849 tcp:8849");
            Runtime.getRuntime()
                    .exec("E:\\work\\soft\\android-sdk-windows\\android-sdk-windows\\tools\\adb.exe forward tcp:8851 tcp:8851");

            Runtime.getRuntime()
                    .exec("E:\\work\\soft\\android-sdk-windows\\android-sdk-windows\\tools\\adb.exe forward tcp:8852 tcp:8852");
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            SocketChannel controlChannel = getSocketConnection(8849);
            SocketChannel fileChannel = getSocketConnection(8851);
            
//            
//            String resetKey = "<?xml version=\"1.0\" encoding=\"utf-8\"?><root><action>ResetKey</action></root>";
//            // 发送数据
//            sendData(controlChannel, resetKey);
//            String resultr = writeDataToFile(controlChannel,10);
//            cipherKey = getRegularSingle("key", resultr,true);
            

            String getSdcardInfo = "<?xml version=\"1.0\" encoding=\"utf-8\"?><root><action>GetSdcardFileName</action><data><dir>"
                    + path + "</dir></data></root>";
            sendData(controlChannel, getSdcardInfo);
            String result = writeDataToFile(controlChannel, 30);

            // 获取path下面文件名和大小
            LinkedList<String> filePathList = getRegular("filePath", result, false);
            LinkedList<String> fileNameList = getRegular("fileName", result, false);
            LinkedList<String> fileLengthList = getRegular("fileLength", result, false);

            // 启用另一个传输通道。
            String startOtherFileTran = "<?xml version=\"1.0\" encoding=\"utf-8\"?><root><action>StartDataPort</action><data><key>8852</key></data></root>";
            sendData(controlChannel, startOtherFileTran);
            result = writeDataToFile(controlChannel, 30);
            System.out.println(result);
            if(!("true".equals(getRegularSingle("result", result, false))))
            {
              return;  
            }
            SocketChannel fileChannel2 = getSocketConnection(8852);

            Long current = System.currentTimeMillis();
            long length = 0;
            int change = 0;
            String checkResult = "";
            for (int i = 0; i < fileNameList.size(); i++) {
                length = Long.valueOf(fileLengthList.get(i));
               
                if (length > 0) {

                    // 混合通道传输
                    
                    sendData(controlChannel, getCheckDownFileMessage(filePathList.get(i)));
                    checkResult = writeDataToFile(controlChannel, 20);
                    System.out.println("hello"+checkResult);
                    if ("CheckLoadFile".equals(getRegularSingle("action", checkResult, false))) {
                        if ("true".equals(getRegularSingle("result", checkResult, false))) {
                            //如果要下载的文件检查存在。                       
                            System.out.println("111 "+filePathList.get(i)+"||" + length);
                            if (change == 0) {
                                //发送下载文件命令。
                                sendData(controlChannel,
                                        getDownFileMessage(8851, filePathList.get(i)));
                                writeDataToFile(controlChannel, 20);
                                //下载文件
                                writeDataToFile(fileChannel, "E:\\test\\" + fileNameList.get(i),
                                        length);
                                change = 1;    
                               
                            } else {
                                sendData(controlChannel,
                                        getDownFileMessage(8852, filePathList.get(i)));
                                writeDataToFile(fileChannel2, "E:\\test\\" + fileNameList.get(i),
                                        length);
                                writeDataToFile(controlChannel, 20);
                                change = 0;
                            }

                        }
                    }

                }
                length = 0;

            }

            System.out.println(" cost time(ms)=" + (System.currentTimeMillis() - current));

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * 从通道中获取文件数据。
     * 
     * @param sc
     * @param path
     * @param fileLength
     */
    private static void writeDataToFile(SocketChannel sc, String path, long fileLength) {

        ByteBuffer r_buff = ByteBuffer.allocate(1024);

        try {
            FileOutputStream fileOutputStream = new FileOutputStream(path, true);
            FileChannel outChannel = fileOutputStream.getChannel();
            r_buff.clear();
            long count = 0;// 用来记录已经传输文件的大小
            int size = 0;
            boolean close = false;
            int kk=0;
            while (!close) {
                while ((size = sc.read(r_buff)) > 0) {
                    count = count + size;
                    // 确保r_buff可读
                    r_buff.flip();
                    kk++;
                    if(kk==100)
                    {
                    System.out.println("down file "+path+"have "+((count*100.00)/fileLength)+"%");
                    kk=0;
                    }
                    if (count < fileLength) {
                        outChannel.write(r_buff);
                    } else if (count == fileLength) {
                        outChannel.write(r_buff);
                        close = true;
                        break;
                    } else {
                        // 防止最后一个缓存中数据大于文件
                        outChannel.write(r_buff, (size - (count - fileLength) - 1));
                        close = true;
                        break;
                    }

                    r_buff.clear();
                }
            }
            close = false;
            count = 0;
            outChannel.close();
            fileOutputStream.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 获取发送下载文件命令
     * 
     * @param port
     * @param path
     * @return
     */
    private static String getDownFileMessage(int port, String path) {

        String message = "<?xml version=\"1.0\" encoding=\"utf-8\"?><root><action>LoadFile</action><data><filePath>"
                + path + "</filePath><key>" + port + "</key></data></root>";
        return message;
    }

    /**
     * 获取检查文件是否存在命令。
     * 
     * @param path
     * @return
     */
    private static String getCheckDownFileMessage(String path) {

        String message = "<?xml version=\"1.0\" encoding=\"utf-8\"?><root><action>CheckLoadFile</action><data><filePath>"
                + path + "</filePath></data></root>";
        return message;
    }

    /**
     * 获取连接端口。
     * 
     * @param port
     * @return
     * @throws IOException
     */
    private static SocketChannel getSocketConnection(int port) throws IOException {
        InetSocketAddress addr = new InetSocketAddress("127.0.0.1", port);//10.204.79.13 127.0.0.1
        // 生成一个socketchannel
        SocketChannel sc = SocketChannel.open();
        // 连接到server
        sc.connect(addr);
        while (!sc.finishConnect())
            ;
        return sc;
    }

    /**
     * 发送数据。
     * 
     * @param sc
     * @param value
     */
    public static boolean sendData(SocketChannel sc, String value) {
        try {
            value=MyDes.encryptDES(value, cipherKey);
            ByteBuffer w_buff = ByteBuffer.allocate(value.length() * 2);
            w_buff.clear();
            w_buff.put(value.getBytes("utf-8"));
            w_buff.flip();
            EchoToService(sc, w_buff);
            w_buff.clear();
            return true;
        } catch (IOException ioe) {
            ioe.printStackTrace();
            return false;
        }
    }

    public static void EchoToService(SocketChannel sc, ByteBuffer write_buff) throws IOException {
        while (write_buff.hasRemaining())
            sc.write(write_buff);
    }

    /**
     * 读取通道中数据，设置超时时间为second秒。
     * 
     * @param sc
     * @param second超时时间。
     * @return
     */
    public static String writeDataToFile(final SocketChannel sc, int second) {

        String result = null;
        ExecutorService executor = Executors.newSingleThreadExecutor();
        FutureTask<String> future = new FutureTask<String>(new Callable<String>() {
            public String call() {
                // 真正的任务在这里执行，这里的返回值类型为String，可以为任意类型
                return getDataFromChannel(sc);
            }
        });
        executor.execute(future);
        // 在这里可以做别的任何事情
        try {
            result = future.get(second, TimeUnit.SECONDS); // 取得结果，同时设置超时执行时间为6秒。同样可以用future.get()，不设置执行超时时间取得结果
        } catch (InterruptedException e) {
            e.printStackTrace();
            future.cancel(true);
        } catch (ExecutionException e) {
            e.printStackTrace();
            future.cancel(true);
        } catch (TimeoutException e) {
            e.printStackTrace();
            future.cancel(true);
        } finally {
            executor.shutdown();
        }
        return result;

    }

    private static final String ENDTAG = "</root>";

    /**
     * 把通道中数据读取到文件流中(如果文件太长，会分几次发送，所以这边要增加处理)。 1：修改解决分包读取问题。
     * 2：解决结束标识符号</root>被分开读取问题。 2：由于缓存中文被分开读取问题。
     * 
     * @param sc
     * @param toFile
     */
    public static String getDataFromChannel(SocketChannel sc) {
        ByteBuffer r_buff = ByteBuffer.allocate(1024);
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        try {
            r_buff.clear();
            int size = 0;
            boolean close = false;
            while (!close) {
                while ((size = sc.read(r_buff)) > 0) {
                    // 确保r_buff可读
                    r_buff.flip();
                    byteOut.write(r_buff.array(), 0, size);
                    // </root> 有可能会被分开读取（由于缓存大小限制是分开读取），所以需要增加判断。
                    byteOut.flush();
                    try{
                    if ((MyDes.decryptDES(byteOut.toString("utf-8"),cipherKey).indexOf(ENDTAG) != -1)) {
                        close = true;
                        r_buff.clear();
                        break;
                    }
                    }catch(Exception e)
                    {
                        
                    }
                    r_buff.clear();
                }
            }
            close = false;

        } catch (Exception e) {
            e.printStackTrace();
        }
        String result = null;
        try {
            byteOut.flush();
            result = MyDes.decryptDES(byteOut.toString("utf-8"),cipherKey);
            byteOut.close();
            byteOut = null;
            r_buff = null;
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return result;

    }

    /**
     * 获取正则表达式数据，比如regularKey是action,那么就是获取<action>AppManager</action><action>hi
     * </action> 中的AppManager和hi，获取所有的数据。
     * 
     * @param regularKey 正则表达式的key。
     * @param source 数据源。
     * @return 如果没有这个数据那么就返回空链表。
     */
    public static LinkedList<String> getRegular(String regularKey, String source, boolean replay) {
        LinkedList<String> result = new LinkedList<String>();
        if (replay) {
            source = source.replaceAll(">\\s*?\r*?\n*?\u0085*?\u2028*?\u2029*?\\s*?<", "><");// 去换行符处理
        }
        Pattern p = Pattern.compile("(?<=<" + regularKey
                + ">)(.*?[\r*?\n*?\u0085*?\u2028*?\u2029*?]*?.*?)(?=</" + regularKey + ">)");
        Matcher m = p.matcher(source);
        while (m.find()) {
            result.add(m.group(0));
        }
        return result;
    }

    /**
     * 通过正则表达式获取xml指令中节点数据。
     * 
     * @param regularKey
     * @param source
     * @param replay
     * @return
     */
    private static String getRegularSingle(String regularKey, String source, boolean replay) {
        String result = null;
        if (replay) {
            source = source.replaceAll(">\\s*?\r*?\n*?\u0085*?\u2028*?\u2029*?\\s*?<", "><");// 去换行符处理
        }
        Pattern p = Pattern.compile("(?<=<" + regularKey
                + ">)(.*?[\r*?\n*?\u0085*?\u2028*?\u2029*?]*?.*?)(?=</" + regularKey + ">)");
        Matcher m = p.matcher(source);
        if (m.find()) {
            result = m.group(0);
        }
        return result;
    }
}
